<?php
/**
* Classe du menu.
*
* Cette classe permet de construire le menu a partir d'un fichier contenant un tableau de
* variable php ou à parti d'un fichier xml. Le menu est généré a partir des méthodes statiques.
* L'instanciation est possible et permet de générer une page affichant les éléments appartenant à un menu lorsque
* celui n'a pas de lien direct.
*
* PHP version 5.1.6
*
* @package   Smartbok
* @author    Christophe Hebet <christophe.hebet@dgfip.finances.gouv.fr>
* @copyright 2013 - DGFiP
*/

class SmartbokMenu extends SmartbokController
{
    const PHP = 1;
    const XML = 2;
    const ERR_MENU_CONSTRUCTION = 'Erreur de construction du menu : plusieurs variables contenues et aucune définie';

    private static $_vars;
    private static $_skipMenuId = array();
    private static $_elementMenuATraiter = array();

    /**
     * Méthode a appeler pour afficher le menu dans les templates
     * @param array $vars
     * @param string $cheminRes
     * @param boolean $urlRewriting
     */
    public static function afficher($vars, $cheminRes, $urlRewriting) {
        self::$_vars = $vars;
        self::$_vars['cheminRes'] = $cheminRes;
        self::$_vars['urlRewriting'] = $urlRewriting;
        if (isset($vars['actif']) === true && $vars['actif'] === true) {
            if ($vars['session'] === true) {
                if (isset($_SESSION['sbkApplicationMenu']) === false ) {
                    $menu = self::construireMenu();
                    $_SESSION['sbkApplicationMenu'] = $menu;
                    return $menu;
                }
                else {
                    return $_SESSION['sbkApplicationMenu'];
                }
            }
            else {
                $menu = self::construireMenu();
                return $menu;
            }
        }
        else {
            return null;
        }
    }

    /**
     * méthode de chargement et de controle du fichier php du menu.
     * @return NULL|array
     */
    private static function _loadFileMenuPhp() {
        include self::$_vars['fichier'];
        $varsInclude = get_defined_vars();
        $keys = (array_keys($varsInclude));
        if (count($varsInclude) === 1) {
            $varMenu = $varsInclude[$keys[0]];
        }
        elseif (isset(self::$_vars['variable']) === true) {
            $varMenu = $varsInclude[self::$_vars['variable']];
        }
        else {

            SmartbokMessage::addMessage(SmartbokMessage::ERR, self::ERR_MENU_CONSTRUCTION);
            return null;
        }
        return $varMenu;
    }

    /**
     * Méthode de construction du menu a partir des paramètres enregistrés.
     * La méthode retourne le menu sous la forme d'une chaine html.
     * @return string
     */
    public static function construireMenu() {
        $idPageActive = null;
        $sClass = null;
        if (isset(self::$_vars['idPageActive']) === true) {
            $idPageActive = self::$_vars['idPageActive'];
        }
        else {
            $sClass = 'element_menu_actif';
        }
        switch (self::$_vars['type']) {
            case self::PHP:
                $varMenu = self::_loadFileMenuPhp();
                self::$_elementMenuATraiter = self::_traiterProfil($varMenu, self::$_vars['profils']);
                $idPageActive = self::_getParentId(self::$_vars['idPageActive'], self::$_elementMenuATraiter);
                $menu = self::_ajouterElement(self::$_elementMenuATraiter, $idPageActive);
            break;

            case self::XML:
                $xml = simplexml_load_file(self::$_vars['fichier']);
                $menu = self::_traiterElementXml($xml, $idPageActive);
            break;

            default:
                return null;
            break;
        }
        $menu = str_replace('<ul></ul>', '', $menu);
        $lien = self::$_vars['cheminRes'].self::$_vars['defaut'];
        $index = "<li class=\"L0 index $sClass\"> <a href=\"$lien\" title=\"Retour à l'accueil\"> </a></li>";
        if (self::$_vars['afficherAccueil'] === false) {
            $index = '';
        }
        return "<h2>menu</h2><ul id=\"menu\">$index$menu</ul>";
    }

    /**
     * Méthode spécifique au traitement du menu sous forme PHP.
     * La méthode est récursive.
     *
     * @param array $elements
     * @param integer $idPageActive
     *
     * @return string
     */
    private static function _ajouterElement($elements, $idPageActive) {
        static $profondeur = 0;
        $html = '';
        foreach ($elements as $key => $menu) {
            if (in_array($key, self::$_skipMenuId) === false) {
                $sClass = null;
                if ($key === $idPageActive) {
                    $sClass = 'element_menu_actif';
                }
                $lien = $menu['lien'];
                $html .= '<li class="L'.$profondeur.' '.$sClass.'"><a href="'.self::_traiterLien($lien, $key).'">';
                $html .= $menu['libelle'].'</a>';
                if (count($menu['enfants']) > 0) {
                    $html .= "<ul>";
                    foreach ($menu['enfants'] as $enfant) {
                        $profondeur++;
                        if (key_exists($enfant, self::$_elementMenuATraiter) === true) {
                            $enfants[$enfant] = self::$_elementMenuATraiter[$enfant];
                            $html .= self::_ajouterElement($enfants, null);
                        }
                        $profondeur--;
                    }
                    $html .= '</ul></li>';
                }
                else {
                    $html .= '</li>';
                }
            }
            if (in_array($key, self::$_skipMenuId) === false) {
                self::$_skipMenuId[] = $key;
            }
        }
        return $html;
    }

    /**
     * Cette méthode fait un prétraitement des éléments du menu php afin d'en exclure les éléments pour lequel
     * l'utilisateur n'est pas habilité.
     *
     * @param array &$elements
     * @param array $profils
     *
     * @return array
     */
    private static function _traiterProfil(&$elements, $profils) {
        $tabElements = array();
        $tabSkipId = array();
        foreach ($elements as $key => $menu) {
            $estDeProfil = false;
            if(isset($menu['profils']) === true && in_array($key, $tabSkipId) === false) {
                foreach ($menu['profils'] as $profil) {
                    if (in_array($profil, $profils) === true) {
                        $tabElements[$key] = $menu;
                        $estDeProfil = true;
                        break;
                    }
                }
            }
            else {
                if (in_array($key, $tabSkipId) === false) {
                    $tabElements[$key] = $menu;
                    $estDeProfil = true;
                }
            }
            if (count($menu['enfants']) > 0 && $estDeProfil === false) {
                $tabSkipId = array_merge($tabSkipId, $menu['enfants']);
            }
        }
        return $tabElements;
    }

    /**
     * Méthode de traitement des élements du menu XML.
     * Récursive
     *
     * @param array $xml
     * @param integer $idPageActive
     * @return string
     */
    private static function _traiterElementXml($xml, $idPageActive) {
        static $profondeur = 0;
        static $arboLien;
        $html = '';
        $key = 1;
        foreach ($xml as $element) {
            $estActif = self::_controllerProfil($element, self::XML);
            if (isset($element->filtre) === true) {
                if (isset($_SESSION[(string)$element->filtre]) === false) {
                    $estActif = false;
                }
            }
            if ($estActif === true) {
                $sClass = null;
                if ($key === $idPageActive) {
                    $sClass = 'element_menu_actif';
                }
                $arboLien[$profondeur] = $key;
                $lien = self::_traiterLien($element->lien, $arboLien);
                if (isset($element->replace) === true) {
                    $element->libelle = $_SESSION['accountInfo'][(string)$element->replace];
                }
                $html .= '<li class="L'.$profondeur.' '.$sClass.'"><a href="'.$lien.'">'.$element->libelle.'</a>';
                if (isset($element->enfants) === true) {
                    $html .= '<ul>';
                    foreach ($element->enfants as $enfant) {
                        $profondeur++;
                        $html .= self::_traiterElementXml($enfant, null);
                        unset($arboLien[$profondeur]);
                        $profondeur--;
                    }
                    $html .= '</ul>';
                }
                $html .= '</li>';
            }
            $key++;
        }
        return $html;
    }

    /**
     * Méthode qui vérifie que l'utilsiateur est habilité pour l'élément à traiter.
     * @param object $element
     * @param interger $type
     * @return boolean
     */
    private static function _controllerProfil($element, $type) {
        switch ($type) {
            case self::PHP:
                if (isset($element['profils']) === true) {
                    foreach ($element['profils'] as $profil) {
                        if (in_array($profil, self::$_vars['profils']) === true) {
                            return true;
                        }
                    }
                    return false;
                }
                else {
                    return true;
                }
            break;

            case self::XML:
                if (isset($element->profils) === true) {
                    foreach ($element->profils->profil as $profil) {
                        if (in_array($profil, self::$_vars['profils']) === true) {
                            return true;
                        }
                    }
                    return false;
                }
                else {
                    return true;
                }
            break;

            default:
                return null;
            break;
        }

    }

    /**
     * Méthode de traitement de lien pour prendre en compte l'url rewriting et les niveaux d'arborescence.
     *
     * @param string $lien
     * @param array $arboLien
     */
    private static function _traiterLien($lien, $arboLien) {
        switch (self::$_vars['type']) {
            case self::PHP:
                if (empty($lien) === true) {
                    if (self::$_vars['urlRewriting'] === true) {
                        return self::$_vars['cheminRes'].'menu/afficherMenu/'.$arboLien;
                    }
                    else {
                        return 'index.php?module=Menu&amp;action=afficherMenu&amp;p='.$arboLien;
                    }
                }
            break;

            case self::XML:
                if (empty($lien) === true) {
                    if (self::$_vars['urlRewriting'] === true) {
                        $menu = self::$_vars['cheminRes'].'menu/afficherMenu/'.implode('/', $arboLien);
                        return $menu;
                    }
                    else {
                        $p = 1;
                        $params = '';
                        foreach ($arboLien as $niveau) {
                            $params .= '&amp;p'.$p.'='.$niveau;
                            $p++;
                        }
                        return 'index.php?module=Menu&amp;action=afficherMenu'.$params;
                    }
                }
            break;

            default:
                return $lien;
            break;
        }
        $pattern = '/&(?![#]?[a-z0-9]+;)/i';
        $lien = self::$_vars['cheminRes'].preg_replace($pattern, '&amp;', $lien);
        return $lien;
    }

    /**
     * Méthode de l'objet qui permet créer la page par défaut des éléments de menu sans lien.
     */
    public function afficherMenu() {
        self::$_vars['profils'] = $this->menu['profils'];
        self::$_vars['cheminRes'] = $this->calculerCheminRessources();
        self::$_vars['urlRewriting'] = $this->urlRewriting;
        $this->addTemplate('smartbok/vues/menu.tpl.php');
        $params = array_values($this->_requete->params);
        $idPageActive = (int) $params[0];
        $menuActif = $this->_getElementMenu($this->menu['type']);
        $this->_page = $menuActif['titre'];
        $this->menuActif = $menuActif;
        $this->menu = array('idPageActive' => $idPageActive);
    }

    /**
     * Méthode de récupération de l'élément du menu sélectionné dans le fichier
     * @param integer $typeMenu
     * @return object
     */
    private function _getElementMenu($typeMenu) {
        if ($typeMenu === self::XML) {
            self::$_vars['type'] = self::XML;
            $elements = simplexml_load_file($this->menu['fichier']);
            $element = $this->_selectElementMenuXml($elements, array_values($this->_requete->params));
            if (is_a($element, 'SimpleXMLElement') === true) {
                $enfantId = 1;
                $menuActif['titre'] = $element->libelle;
                if (isset($element->enfants) === true) {
                    foreach ($element->enfants[0] as $key => $enfant) {
                        if(self::_controllerProfil($enfant, self::XML) === true) {
                            $lien['libelle'] = (string) $enfant->libelle;
                            $params = $this->_requete->params;
                            $params[] = $enfantId;
                            $lien['lien'] = self::_traiterLien($enfant->lien, $params);
                            $menuActif['liens'][] = $lien;
                        }
                        $enfantId++;
                    }
                }
                if (isset($menuActif['liens']) === false) {
                    $menuActif['liens'] = array();
                    SmartbokMessage::addMessage(SmartbokMessage::INFO, 'Aucune option accessible.');
                }
                return $menuActif;
            }
            else {
                SmartbokMessage::addMessage(SmartbokMessage::ERR, 'Menu inexistant ou habilitation insuffisante.');
                $menuActif['titre'] = 'Erreur de construction de la page';
                $menuActif['liens'] = array();
                return $menuActif;
            }

        }
        if ($typeMenu === self::PHP) {
            self::$_vars['type'] = self::PHP;
            self::$_vars['fichier'] = $this->menu['fichier'];
            $varMenu = self::_loadFileMenuPhp();
            $elements = self::_traiterProfil($varMenu, self::$_vars['profils']);
            $param = array_values($this->_requete->params);
            if (isset($elements[$param[0]]) === true) {
                $element = $varMenu[$param[0]];
                $menuActif['titre'] = $element['libelle'];
                if (isset($element['enfants']) === true) {
                    foreach ($element['enfants'] as $enfant) {
                        if (self::_controllerProfil($varMenu[$enfant], self::PHP) === true) {
                            $lien['libelle'] = $varMenu[$enfant]['libelle'];
                            $lien['lien'] = self::_traiterLien($varMenu[$enfant]['lien'], $enfant);
                            $menuActif['liens'][] = $lien;
                        }
                    }
                    if (isset($menuActif['liens']) === false) {
                        $menuActif['liens'] = array();
                        SmartbokMessage::addMessage(SmartbokMessage::INFO, 'Aucune option accessible.');
                    }
                    return $menuActif;
                }

            }
            else {
                SmartbokMessage::addMessage(SmartbokMessage::ERR, 'Menu inexistant ou habilitation insuffisante.');
                $menuActif['titre'] = 'Erreur de construction de la page';
                $menuActif['liens'] = array();
                return $menuActif;
            }
        }
    }

    /**
     * Méthode de sélection de l'élément actif du menu.
     * Récursive
     * @param array $elements
     * @param array $hierarchie
     */
    private function _selectElementMenuXml($elements, $hierarchie) {
        $indice = (int) $hierarchie[0];
        $actif = null;
        $index = 1;
        foreach ($elements as $key => $element) {
            if (self::_controllerProfil($element, self::XML) === true) {
                if ($indice === $index) {
                    $actif = $element;
                    array_shift($hierarchie);
                    if (isset($element->enfants) === true && count($hierarchie) > 0) {
                        $actif = $this->_selectElementMenuXml($element->enfants[0], $hierarchie);
                    }
                }
            }
            $index++;
        }
        return $actif;
    }

    /**
     * méthode qui permet de récupérer l'identifiant du parent le plus éloigné pour l'activer dans le menu.
     * Recursive
     * @param integer $id
     * @param array $elements
     */
    private static function _getParentId($id, $elements) {
        foreach($elements as $key => $element) {
            if (isset ($element['enfants']) === true) {
                if (in_array($id, $element['enfants']) === true) {
                    $id = self::_getParentId($key, $elements);
                }
            }
        }
        return $id;
    }
}